﻿#region

using System.Reflection;
using FreeRedis;
using Microsoft.Extensions.Logging;
using Vin.Extension.Cache;
using Vin.Extension.Redis.Entities;
using Vin.Extension.Redis.Extension;
using Vin.Tool.AspNetCore.Singleton;
using Vin.Tool.Core.AssemblyCategory;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Core.BasicTypeCategory;
using Vin.Tool.Core.ConvertCategory;
using Vin.Tool.Core.JsonCategory;

#endregion

namespace Vin.Extension.Redis.Cache;

public class RedisCache : ICache
{
    public RedisClient RedisClient { get; private set; } = null!;

    private ILogger<RedisCache> _logger;

    private Timer _timer;

    private string _connectString;

    private ConnectionStringBuilder[] _slaveConnectionStrings;

    public RedisCache(string connectString, params ConnectionStringBuilder[] slaveConnectionStrings)
    {
        _connectString = connectString;
        _slaveConnectionStrings = slaveConnectionStrings;
        Init();
        _logger = VinApp.GetRequiredService<ILogger<RedisCache>>();
        _timer = new Timer(KeepAlive, null, TimeSpan.Zero, TimeSpan.FromMinutes(2));
    }

    private void KeepAlive(object? state)
    {
        try
        {
            RedisClient.Ping();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Redis连接异常");
            Init();
        }
    }

    public void Init()
    {
        RedisClient = new RedisClient(_connectString, _slaveConnectionStrings);
    }

    #region 基本操作

    public ScanResult<string> Scan(long cursor, string pattern, long count, string type = "")
    {
        return RedisClient.Scan(cursor, pattern, count, type);
    }

    public async Task<ScanResult<string>> ScanAsync(long cursor, string pattern, long count, string type = "")
    {
        return await RedisClient.ScanAsync(cursor, pattern, count, type);
    }

    public bool SetCache<T>(string key, T value, TimeSpan? expireTime = null)
    {
        RedisClient.Set(key, value.ToCamelCaseJson(), expireTime ?? TimeSpan.Zero);
        return true;
    }

    public async Task<bool> SetCacheAsync<T>(string key, T value, TimeSpan? expireTime = null)
    {
        await RedisClient.SetAsync(key, value.ToCamelCaseJson(), expireTime ?? TimeSpan.Zero);
        return true;
    }

    public async Task<bool> SetCacheNoExistsAsync<T>(string key, T value, TimeSpan? expireTime = null)
    {
        if (!RedisClient.Exists(key))
        {
            await SetCacheAsync(key, value, expireTime);
        }

        return true;
    }

    public T? GetCache<T>(string key)
    {
        var value = RedisClient.Get(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public async Task<T?> GetCacheAsync<T>(string key)
    {
        var value = await RedisClient.GetAsync(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public string GetCache(string key)
    {
        return RedisClient.Get(key);
    }

    public async Task<string> GetCacheAsync(string key)
    {
        return await RedisClient.GetAsync(key);
    }

    public bool RemoveCache(params string[] keys)
    {
        return RedisClient.Del(keys) > 0;
    }

    public bool RemoveCache(string key)
    {
        return RemoveCache(new[] { key });
    }

    public bool RemoveCache(List<string> keys)
    {
        return RemoveCache(keys.ToArray());
    }

    public async Task<bool> RemoveCacheAsync(params string[] keys)
    {
        return await RedisClient.DelAsync(keys) > 0;
    }

    public async Task<bool> RemoveCacheAsync(string key)
    {
        return await RemoveCacheAsync(new[] { key });
    }

    public async Task<bool> RemoveCacheAsync(List<string> keys)
    {
        return await RemoveCacheAsync(keys.ToArray());
    }

    public bool Exists(string key)
    {
        return RedisClient.Exists(key);
    }

    public async Task<bool> ExistsAsync(string key)
    {
        return await RedisClient.ExistsAsync(key);
    }

    public T? GetRemoveCache<T>(string key)
    {
        // var result = GetCache<T>(key);
        // RemoveCache(key);
        var result = RedisClient.GetDel(key);
        return result.IsNullOrEmpty() ? default : result.ToCamelCaseObject<T?>();
    }

    public async Task<T?> GetRemoveCacheAsync<T>(string key)
    {
        // var result = await GetCacheAsync<T>(key);
        // await RemoveCacheAsync(key);
        // return result;
        var result = await RedisClient.GetDelAsync(key);
        return result.IsNullOrEmpty() ? default : result.ToCamelCaseObject<T?>();
    }

    #endregion

    #region 过期时间

    /// <summary>
    /// 设置过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <param name="expireTime"></param>
    /// <returns></returns>
    public bool Expire(string key, TimeSpan expireTime)
    {
        return RedisClient.Expire(key, expireTime);
    }

    /// <summary>
    /// 设置过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <param name="expireTime"></param>
    /// <returns></returns>
    public async Task<bool> ExpireAsync(string key, TimeSpan expireTime)
    {
        return await RedisClient.ExpireAsync(key, expireTime);
    }

    /// <summary>
    /// 设置过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <param name="expireTime"></param>
    /// <returns></returns>
    public bool ExpireAt(string key, DateTime expireTime)
    {
        return RedisClient.ExpireAt(key, expireTime);
    }

    /// <summary>
    /// 设置过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <param name="expireTime"></param>
    /// <returns></returns>
    public async Task<bool> ExpireAtAsync(string key, DateTime expireTime)
    {
        return await RedisClient.ExpireAtAsync(key, expireTime);
    }

    /// <summary>
    /// 获取过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public TimeSpan? Ttl(string key)
    {
        return TimeSpan.FromSeconds(RedisClient.Ttl(key));
    }

    /// <summary>
    /// 获取过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public async Task<TimeSpan?> TtlAsync(string key)
    {
        return TimeSpan.FromSeconds(await RedisClient.TtlAsync(key));
    }

    #endregion

    #region Hash

    /// <summary>
    /// 设置哈希缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="field">属性</param>
    /// <param name="value">值</param>
    /// <param name="expireTime">过期时间</param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public bool SetHashCache<T>(string key, string? field, T value, TimeSpan? expireTime = null)
    {
        RedisClient.HSet(key, field, value.ToCamelCaseJson());
        if (expireTime.HasValue)
        {
            RedisClient.Expire(key, expireTime.Value);
        }

        return true;
    }

    /// <summary>
    /// 设置哈希缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="field">属性</param>
    /// <param name="value">值</param>
    /// <param name="expireTime">过期时间</param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<bool> SetHashCacheAsync<T>(string key, string? field, T value, TimeSpan? expireTime = null)
    {
        await RedisClient.HSetAsync(key, field, value.IsValueType() ? value : (object)value.ToCamelCaseJson());
        if (expireTime.HasValue)
        {
            await RedisClient.ExpireAsync(key, expireTime.Value);
        }

        return true;
    }

    /// <summary>
    /// 设置哈希缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="values"></param>
    /// <param name="expireTime"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<bool> SetHashCacheAsync<T>(string key, Dictionary<string, T> values, TimeSpan? expireTime = null)
    {
        var dic = values.ToDictionary(x => x.Key, x => x.Value);
        await RedisClient.HMSetAsync(key, dic);
        if (expireTime.HasValue)
        {
            await RedisClient.ExpireAsync(key, expireTime.Value);
        }

        return true;
    }

    /// <summary>
    /// 获取哈希缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="field"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T? GetHashCache<T>(string key, string? field)
    {
        var value = RedisClient.HGet(key, field);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// 获取哈希缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="field"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<T?> GetHashCacheAsync<T>(string key, string? field)
    {
        var value = await RedisClient.HGetAsync(key, field);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public bool RemoveHashCache(string key, string field)
    {
        return RedisClient.HDel(key, field) > 0;
    }

    public async Task<bool> RemoveHashCacheAsync(string key, string field)
    {
        return await RedisClient.HDelAsync(key, field) > 0;
    }

    public long GetHashCount(string key)
    {
        return RedisClient.HLen(key);
    }

    public async Task<long> GetHashCountAsync(string key)
    {
        return await RedisClient.HLenAsync(key);
    }

    public bool HashExists(string key, string field)
    {
        return RedisClient.HExists(key, field);
    }

    public async Task<bool> HashExistsAsync(string key, string field)
    {
        return await RedisClient.HExistsAsync(key, field);
    }

    public Dictionary<string, string> HashAll(string key)
    {
        return RedisClient.HGetAll(key);
    }

    public async Task<Dictionary<string, string>> HashAllAsync(string key)
    {
        return await RedisClient.HGetAllAsync(key);
    }

    #endregion

    #region 原子操作

    /// <summary>
    /// 自增
    /// </summary>
    public long Incr(string key, TimeSpan? expireTime = null, bool firstSetExpire = true)
    {
        var value = RedisClient.Incr(key);
        if (value == 1 && expireTime.HasValue && firstSetExpire)
        {
            RedisClient.Expire(key, expireTime.Value);
        }

        return value;
    }

    /// <summary>
    /// 自增
    /// </summary>
    public async Task<long> IncrAsync(string key, TimeSpan? expireTime = null, bool firstSetExpire = true)
    {
        var value = await RedisClient.IncrAsync(key);
        if (value == 1 && expireTime.HasValue && firstSetExpire)
        {
            await RedisClient.ExpireAsync(key, expireTime.Value);
        }

        return value;
    }

    /// <summary>
    /// 自减
    /// </summary>
    public long Decr(string key, TimeSpan? expireTime = null, bool firstSetExpire = true)
    {
        var value = RedisClient.Decr(key);
        if (value == -1 && expireTime.HasValue && firstSetExpire)
        {
            RedisClient.Expire(key, expireTime.Value);
        }

        return value;
    }

    /// <summary>
    /// 自减
    /// </summary>
    public async Task<long> DecrAsync(string key, TimeSpan? expireTime = null, bool firstSetExpire = true)
    {
        var value = await RedisClient.DecrAsync(key);
        if (value == 1 && expireTime.HasValue && firstSetExpire)
        {
            await RedisClient.ExpireAsync(key, expireTime.Value);
        }

        return value;
    }

    /// <summary>
    /// 哈希自增
    /// </summary>
    public long HIncrBy(string key, string field, long value)
    {
        return RedisClient.HIncrBy(key, field, value);
    }

    /// <summary>
    /// 哈希自增
    /// </summary>
    public async Task<long> HIncrByAsync(string key, string? field, long value)
    {
        return await RedisClient.HIncrByAsync(key, field, value);
    }

    /// <summary>
    /// 哈希自增
    /// </summary>
    public long HIncrBy<T>(string key, T field, long value) where T : struct
    {
        return RedisClient.HIncrBy(key, field.ToString(), value);
    }

    /// <summary>
    /// 哈希自增
    /// </summary>
    public async Task<long> HIncrByAsync<T>(string key, T field, long value) where T : struct
    {
        return await RedisClient.HIncrByAsync(key, field.ToString(), value);
    }

    #endregion

    #region List

    /// <summary>
    /// Push到列表头部
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public bool LPush<T>(string key, T value)
    {
        return RedisClient.LPush(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    /// <summary>
    /// Push到列表头部
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<bool> LPushAsync<T>(string key, T value)
    {
        return await RedisClient.LPushAsync(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    /// <summary>
    /// Push到列表尾部
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public bool RPush<T>(string key, T value)
    {
        return RedisClient.RPush(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    /// <summary>
    /// Push到列表尾部
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<bool> RPushAsync<T>(string key, T value)
    {
        return await RedisClient.RPushAsync(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    /// <summary>
    /// Pop列表头部
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T? LPop<T>(string key)
    {
        var value = RedisClient.LPop(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// Pop列表头部
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<T?> LPopAsync<T>(string key)
    {
        var value = await RedisClient.LPopAsync(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// Pop列表尾部
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T? RPop<T>(string key)
    {
        var value = RedisClient.RPop(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// Pop列表尾部
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<T?> RPopAsync<T>(string key)
    {
        var value = await RedisClient.RPopAsync(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// 移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeout"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T? BlPop<T>(string key, int timeout)
    {
        var value = RedisClient.BLPop(key, timeout);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// 移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeout"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<T?> BlPopAsync<T>(string key, int timeout)
    {
        var value = await RedisClient.BLPopAsync(key, timeout);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// 移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeout"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T? BrPop<T>(string key, int timeout)
    {
        var value = RedisClient.BRPop(key, timeout);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// 移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeout"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<T?> BrPopAsync<T>(string key, int timeout)
    {
        var value = await RedisClient.BRPopAsync(key, timeout);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    /// <summary>
    /// 获取列表长度
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public long LLen(string key)
    {
        return RedisClient.LLen(key);
    }

    /// <summary>
    /// 获取列表长度
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public async Task<long> LLenAsync(string key)
    {
        return await RedisClient.LLenAsync(key);
    }

    /// <summary>
    /// 获取列表指定范围的元素
    /// </summary>
    /// <param name="key"></param>
    /// <param name="start"></param>
    /// <param name="stop"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public List<T?> LRange<T>(string key, long start, long stop)
    {
        return RedisClient.LRange(key, start, stop).Select(x => x.IsNullOrEmpty() ? default : x.ToCamelCaseObject<T?>())
            .ToList();
    }

    /// <summary>
    /// 获取列表指定范围的元素
    /// </summary>
    /// <param name="key"></param>
    /// <param name="start"></param>
    /// <param name="stop"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<List<T?>> LRangeAsync<T>(string key, long start, long stop)
    {
        return (await RedisClient.LRangeAsync(key, start, stop))
            .Select(x => x.IsNullOrEmpty() ? default : x.ToCamelCaseObject<T?>()).ToList();
    }

    public long LRem<T>(string key, long count, T value)
    {
        return RedisClient.LRem(key, count, value);
    }

    public async Task<long> LRemAsync<T>(string key, long count, T value)
    {
        return await RedisClient.LRemAsync(key, count, value);
    }

    #endregion

    #region Set

    public bool SAdd<T>(string key, T value)
    {
        return RedisClient.SAdd(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    public bool SAdd<T>(string key, List<T> values)
    {
        var result = new List<bool>();
        foreach (var value in values)
        {
            result.Add(RedisClient.SAdd(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0);
        }

        return result.Any(x => x);
    }

    public async Task<bool> SAddAsync<T>(string key, T value)
    {
        return await RedisClient.SAddAsync(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    public async Task<bool> SAddAsync<T>(string key, List<T> values)
    {
        var result = new List<bool>();
        foreach (var value in values)
        {
            result.Add(await RedisClient.SAddAsync(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0);
        }

        return result.Any(x => x);
    }

    public bool SRem<T>(string key, T value)
    {
        return RedisClient.SRem(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    public async Task<bool> SRemAsync<T>(string key, T value)
    {
        return await RedisClient.SRemAsync(key, value.IsValueType() ? value : value.ToCamelCaseJson()) > 0;
    }

    public bool SIsMember<T>(string key, T value)
    {
        return RedisClient.SIsMember(key, value.IsValueType() ? value : (object)value.ToCamelCaseJson());
    }

    public async Task<bool> SIsMemberAsync<T>(string key, T value)
    {
        return await RedisClient.SIsMemberAsync(key, value.IsValueType() ? value : (object)value.ToCamelCaseJson());
    }

    public long SCard(string key)
    {
        return RedisClient.SCard(key);
    }

    public async Task<long> SCardAsync(string key)
    {
        return await RedisClient.SCardAsync(key);
    }

    public List<T?> SMembers<T>(string key)
    {
        return RedisClient.SMembers(key).Select(x => x.IsNullOrEmpty() ? default : x.ToCamelCaseObject<T?>()).ToList();
    }

    public async Task<List<T?>> SMembersAsync<T>(string key)
    {
        return (await RedisClient.SMembersAsync(key))
            .Select(x => x.IsNullOrEmpty() ? default : x.ToCamelCaseObject<T?>()).ToList();
    }

    public T? SPop<T>(string key)
    {
        var value = RedisClient.SPop(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public async Task<T?> SPopAsync<T>(string key)
    {
        var value = await RedisClient.SPopAsync(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public T? SRandMember<T>(string key)
    {
        var value = RedisClient.SRandMember(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public async Task<T?> SRandMemberAsync<T>(string key)
    {
        var value = await RedisClient.SRandMemberAsync(key);
        return value.IsNullOrEmpty() ? default : value.ToCamelCaseObject<T?>();
    }

    public List<T?>? SRandMember<T>(string key, int count)
    {
        var value = RedisClient.SRandMember(key, count);
        return value.Length <= 0
            ? default
            : value.Select(x => x.IsNullOrEmpty() ? default : x.ToCamelCaseObject<T>()).ToList();
    }

    public async Task<List<T?>?> SRandMemberAsync<T>(string key, int count)
    {
        var value = await RedisClient.SRandMemberAsync(key, count);
        return value.Length <= 0
            ? default
            : value.Select(x => x.IsNullOrEmpty() ? default : x.ToCamelCaseObject<T>()).ToList();
    }

    #endregion

    #region ZAdd

    /// <summary>
    /// 设置有序集合
    /// </summary>
    public long SetScoreCache(string key, string member, decimal score)
    {
        return RedisClient.ZAdd(key, score, member);
    }

    public async Task<long> SetScoreCacheAsync(string key, string member, decimal score)
    {
        return await RedisClient.ZAddAsync(key, score, member);
    }

    public long SetScoreCache<T>(string key, T member, decimal score)
    {
        return RedisClient.ZAdd(key, score, member?.ToString());
    }

    public async Task<long> SetScoreCacheAsync<T>(string key, T member, decimal score)
    {
        return await RedisClient.ZAddAsync(key, score, member?.ToString());
    }

    public decimal? GetScore(string key, string? member)
    {
        return RedisClient.ZScore(key, member);
    }

    public async Task<decimal?> GetScoreAsync(string key, string? member)
    {
        return await RedisClient.ZScoreAsync(key, member);
    }

    public decimal? GetScore(string key, long? member)
    {
        return GetScore(key, member.ToString());
    }

    public async Task<decimal?> GetScoreAsync(string key, long? member)
    {
        return await GetScoreAsync(key, member.ToString());
    }

    /// <summary>
    /// 获取指定分数范围的成员
    /// </summary>
    public List<string> GetScoreCacheByScore(string key, decimal min, decimal max, int offset = 0, int count = 0)
    {
        return RedisClient.ZRangeByScore(key, min, max, offset, count).ToList();
    }

    public async Task<List<string>> GetScoreCacheByScoreAsync(string key, decimal min, decimal max, int offset = 0,
        int count = 0)
    {
        return (await RedisClient.ZRangeByScoreAsync(key, min, max, offset, count)).ToList();
    }

    /// <summary>
    /// 获取指定索引范围的成员
    /// </summary>
    public List<string> GetScoreCacheByIndex(string key, long start, long stop)
    {
        return RedisClient.ZRange(key, start, stop).ToList();
    }

    public async Task<List<string>> GetScoreCacheByIndexAsync(string key, long start, long stop)
    {
        return (await RedisClient.ZRangeAsync(key, start, stop)).ToList();
    }

    public List<ZMember> GetScoreCacheByIndexWithScore(string key, long start, long stop)
    {
        return RedisClient.ZRangeWithScores(key, start, stop).ToList();
    }

    public async Task<List<ZMember>> GetScoreCacheByIndexWithScoreAsync(string key, long start, long stop)
    {
        return (await RedisClient.ZRangeWithScoresAsync(key, start, stop)).ToList();
    }

    /// <summary>
    /// 获取指定索引范围的成员
    /// </summary>
    public List<string> GetScoreCacheByIndexRev(string key, long start, long stop)
    {
        return RedisClient.ZRevRange(key, start, stop).ToList();
    }

    public async Task<List<string>> GetScoreCacheByIndexRevAsync(string key, long start, long stop)
    {
        return (await RedisClient.ZRevRangeAsync(key, start, stop)).ToList();
    }

    /// <summary>
    /// 为有序集 key 的成员 member 的 score 值加上增量 score
    /// </summary>
    public decimal IncrScoreCache(string key, string member, decimal score)
    {
        return RedisClient.ZIncrBy(key, score, member);
    }

    public async Task<decimal> IncrScoreCacheAsync(string key, string member, decimal score)
    {
        return await RedisClient.ZIncrByAsync(key, score, member);
    }

    /// <summary>
    /// 移除指定成员
    /// </summary>
    public long RemoveScoreCache(string key, string? member)
    {
        return RedisClient.ZRem(key, member);
    }

    public async Task<long> RemoveScoreCacheAsync(string key, string? member)
    {
        return await RedisClient.ZRemAsync(key, member);
    }

    public long RemoveScoreCache(string key, long? member)
    {
        return RemoveScoreCache(key, member.ToString());
    }

    public async Task<long> RemoveScoreCacheAsync(string key, long? member)
    {
        return await RemoveScoreCacheAsync(key, member.ToString());
    }

    public long RemoveScoreCache(string key, List<string> member)
    {
        return RedisClient.ZRem(key, member.ToArray());
    }

    public async Task<long> RemoveScoreCacheAsync(string key, List<string> member)
    {
        return await RedisClient.ZRemAsync(key, member.ToArray());
    }

    public long RemoveScoreCache(string key, List<long> member)
    {
        return RedisClient.ZRem(key, member.Select(m => m.ToString()).ToArray());
    }

    public async Task<long> RemoveScoreCacheAsync(string key, List<long> member)
    {
        return await RedisClient.ZRemAsync(key, member.Select(m => m.ToString()).ToArray());
    }

    /// <summary>
    /// 移除指定分数范围的成员
    /// </summary>
    public long RemoveScoreCacheByScore(string key, decimal min, decimal max)
    {
        return RedisClient.ZRemRangeByScore(key, min, max);
    }

    public async Task<long> RemoveScoreCacheByScoreAsync(string key, decimal min, decimal max)
    {
        return await RedisClient.ZRemRangeByScoreAsync(key, min, max);
    }

    /// <summary>
    /// 获取成员数量
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public long MemberCount(string key)
    {
        return RedisClient.ZCard(key);
    }

    public async Task<long> MemberCountAsync(string key)
    {
        return await RedisClient.ZCardAsync(key);
    }

    public long MemberCountByScore(string key, decimal min, decimal max)
    {
        return RedisClient.ZCount(key, min, max);
    }

    public async Task<long> MemberCountByScoreAsync(string key, decimal min, decimal max)
    {
        return await RedisClient.ZCountAsync(key, min, max);
    }

    public async Task<long> MemberCountByScoreAsync(string key, string min, string max)
    {
        return await RedisClient.ZCountAsync(key, min, max);
    }

    public async Task<long> MemberCountByMinScoreAsync(string key, decimal min)
    {
        return await RedisClient.ZCountAsync(key, min + "", "+inf");
    }

    public async Task<long> MemberCountByMinScoreAsync(string key, string min)
    {
        return await RedisClient.ZCountAsync(key, min, "+inf");
    }

    public async Task<long> MemberCountByMaxScoreAsync(string key, decimal max)
    {
        return await RedisClient.ZCountAsync(key, "-inf", max + "");
    }

    public async Task<long> MemberCountByMaxScoreAsync(string key, string max)
    {
        return await RedisClient.ZCountAsync(key, "-inf", max);
    }

    #endregion

    #region SetNx

    public bool SetNx(string key, string value, TimeSpan? expireTime = null)
    {
        return RedisClient.SetNx(key, value, expireTime ?? TimeSpan.Zero);
    }

    public async Task<bool> SetNxAsync(string key, string value, TimeSpan? expireTime = null)
    {
        return await RedisClient.SetNxAsync(key, value, expireTime ?? TimeSpan.Zero);
    }

    #endregion

    #region 坐标

    #region 添加坐标

    public async Task<bool> SetGeoAsync(string key, decimal longitude, decimal latitude, object member)
    {
        var geoMember = new GeoMember(longitude, latitude, member.ToString());
        return await SetGeoAsync(key, geoMember);
    }

    public async Task<bool> SetGeoAsync(string key, GeoMember member)
    {
        return await SetGeoAsync(key, new List<GeoMember>() { member });
    }

    public async Task<bool> SetGeoAsync(string key, List<GeoMember> members)
    {
        return await SetGeoAsync(key, members.ToArray());
    }

    public async Task<bool> SetGeoAsync(string key, GeoMember[] members)
    {
        return await RedisClient.GeoAddAsync(key, members) > 0;
    }

    public async Task<decimal?> GeoDistAsync(string key, string member1, string member2, GeoUnit unit = GeoUnit.m)
    {
        return await RedisClient.GeoDistAsync(key, member1, member2, unit);
    }

    // public async Task<decimal?> GeoDistAsync(decimal lat1, decimal lon1, decimal lat2, decimal lon2){}

    #endregion

    #region 根据坐标获取附近

    public async Task<List<GeoSearchResult>> GeoSearchAsync(string key, GeoSearchType searchType,
        GeoSearchShape searchShape,
        GeoUnit unit = GeoUnit.m,
        bool withCoord = false,
        bool withDist = false,
        bool withHash = false,
        GeoSearchPage? searchPage = null,
        int? count = null,
        Collation collation = Collation.asc)
    {
        var cmd = new CommandPacket("GEOSEARCH")
            .InputKey(key)
            .InputIf(searchType.Type == GeoSearchType.SearchType.Frommember, "FROMMEMBER", searchType.Member)
            .InputIf(searchType.Type == GeoSearchType.SearchType.Fromlonlat, "FROMLONLAT", searchType.Lon,
                searchType.Lat)
            .InputIf(searchShape.Shape == GeoSearchShape.SearchShape.Byradius, "BYRADIUS", searchShape.Radius, unit)
            .InputIf(searchShape.Shape == GeoSearchShape.SearchShape.Bybox, "BYBOX", searchShape.Width,
                searchShape.Height, unit)
            .InputRaw(collation)
            .InputIf(count.HasValue, "COUNT", count)
            .InputIf(withCoord, "WITHCOORD")
            .InputIf(withDist, "WITHDIST")
            .InputIf(withHash, "WITHHASH");

        var objResult = await RedisClient.CallAsync(cmd) as object[] ?? new object[] { };
        if (searchPage != null)
        {
            searchPage.TotalCount = objResult.Length;
            objResult = objResult.Skip((searchPage.Page - 1) * searchPage.PageSize).Take(searchPage.PageSize).ToArray();
        }

        return objResult.Select(m =>
        {
            var item = m as object[] ?? new object[] { };
            var res = new GeoSearchResult(item[0].ToString()!);
            int num = 0;
            if (withDist)
                res.Distance = item[++num].ToDecimal();
            if (withHash)
                res.Hash = item[++num].ToLong();
            if (withCoord)
            {
                var numArray = item[num + 1] as object[];
                res.Longitude = numArray?[0].ToDecimal();
                res.Latitude = numArray?[1].ToDecimal();
            }

            return res;
        }).ToList();
    }

    #endregion

    #endregion

    #region Lua

    #region 原生Eval

    public object Eval(string script, string[]? keys = null, params object[] args)
    {
        return RedisClient.Eval(script, keys, args);
    }

    public async Task<object> EvalAsync(string script, string[]? keys = null, params object[] args)
    {
        return await RedisClient.EvalAsync(script, keys, args);
    }

    public object Eval(string script, List<string>? keys = null, params object[] args)
    {
        return RedisClient.Eval(script, keys?.ToArray(), args);
    }

    public async Task<object> EvalAsync(string script, List<string>? keys = null, params object[] args)
    {
        return await RedisClient.EvalAsync(script, keys?.ToArray(), args);
    }

    #endregion

    #region Lua脚本

    public async Task<object?> LuaFileAsync(string filePath, List<string>? keys = null, params object[] args)
    {
        if (!File.Exists(filePath)) return null;
        var script = File.ReadAllText(filePath);
        if (string.IsNullOrEmpty(script)) return null;
        return await EvalAsync(script, keys, args);
    }

    public async Task<object?> LuaResourceFileAsync(Assembly assembly, string resourceName, List<string>? keys = null,
        params object[] args)
    {
        var script = await AssemblyTool.GetManifestResourceStringAsync(resourceName, assembly);
        if (script == null) return null;
        return await EvalAsync(script, keys, args);
    }

    #endregion

    #endregion
}